#include "defines.h"
#include "serial.h"
#include "lib.h"
#include "xmodem.h"
#include "elf.h"
#include "interrupt.h"
#include "dram.h"
#include "delay.h"

static int init(void)
{
    extern int erodata, data_start, edata, bss_start, ebss;
    
    memcpy(&data_start, &erodata, (long)&edata - (long)&data_start);
    memset(&bss_start, 0, (long)&ebss - (long)&bss_start);

    softvec_init();
    
    serial_init(SERIAL_DEFAULT_DEVICE);
        
    dram_init();

    return 0;
}

static int dump(char * buf, long size)
{
    long i;
    
    if (size < 0) {
        puts("no data.\n");
        return -1;
    }
    
    for (i = 0; i < size; i++) {
        putxval(buf[i], 2);
        if ((i & 0xf) == 0xf) {
            puts("\n");
        } else {
            if ((i & 0xf) == 7)
                puts(" ");
            puts(" ");
        }
    }
    puts("\n");
    
    return 0;
}

static char *COMMANDS[] = {"load", "dump", "run", "boot", "help", NULL};

int main(void)
{
    static char buf[16];
    static long size = -1;
    static unsigned char *loadbuf = NULL;
    extern int buffer_start, bootbuf_start;
    char *entry_point;
    void (*f)(void);

    INTR_DISABLE;
    
    init();

    puts("kzload (KOZOS boot loader) started.\n");
    
    while (1) {
        int i;
        
        puts("kzload> ");
        
        i = gets(buf);
        
        if (i == -1) {
            puts("serial error !!\n");
            serial_error_clear(SERIAL_DEFAULT_DEVICE);
            continue;
        }
        
        if (!strcmp(buf, "load")) {
            loadbuf = (char *)(&buffer_start);
            size = xmodem_recv(loadbuf);
            
            delay_s(1); /* もしかするとputsがXMODEMに行ってしまうから? */
            
            if (size < 0) {
                puts("\nXMODEM receive error!\n");
            } else {
                puts("\nXMODEM receive succeeded.\n");
            }
            
        } else if (!strcmp(buf, "dump")) {
            puts("size: "); putxval(size, 0); puts("\n");
            dump(loadbuf, size);        
            
        } else if (!strcmp(buf, "run")) {
            entry_point = elf_load(loadbuf);
            if(!entry_point) {
                puts("run error!\n");
            } else {
                puts("starting from enrty point: ");
                putxval((unsigned long)entry_point, 0);
                puts("\n");
                f = (void (*)(void))entry_point;
                f();
            }
            
        } else if (!strcmp(buf, "boot")) { /* 転送しながら直接ロード */
            loadbuf = (char *)(&bootbuf_start);
            entry_point = elf_boot(loadbuf, loadbuf + XMODEM_BLOCK_SIZE);
            
            delay_s(1);
            
            if(!entry_point) {
                puts("boot error!\n");
            } else {
                puts("starting from enrty point: ");
                putxval((unsigned long)entry_point, 0);
                puts("\n");
                f = (void (*)(void))entry_point;
                f();
            }

        } else if (!strcmp(buf, "help")) {
            char **p;
            for(p = COMMANDS; *p; p++) {
                puts(*p);
                puts("\n");
            }
        
        } else {
            puts("unknown command.\n");
        }
    }
        
    return 0;
}
